/*
 * @Author: xuetao 3168581151@qq.com
 * @Date: 2025-01-04
 * @LastEditors: xuetao 3168581151@qq.com
 * @LastEditTime: 2025-04-16
 * @Description: 单例模式，网络请求
 */
import 'dart:async';

import 'package:chat_client/router/router.dart';
import 'package:chat_client/utils/show_messae.dart';
import 'package:chat_client/widgets/c_loading.dart';
import 'package:dio/dio.dart';
import 'package:chat_client/repositories/auth_repository.dart';

import 'package:chat_client/config/api_config.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

class ApiService {
  final AuthRepository _authRepository = AuthRepository.instance;
  // 1. 声明一个静态私有实例
  static final ApiService _instance = ApiService._internal();

  // 2. getter
  static ApiService get instance => _instance;

  // 新增状态控制变量
  bool _isRefreshing = false;
  final List<Completer<void>> _pendingRequests = [];

  // 3. 私有构造函数
  ApiService._internal() {
    _dio.interceptors.add(InterceptorsWrapper(
      /**
       * 请求拦截
       */
      onRequest: (options, handler) async {
        if (options.uri.path == '/auth/login' ||
            options.uri.path == '/auth/register' ||
            options.uri.path == '/auth/capcha' ||
            options.uri.path == '/auth/refresh') {
          return handler.next(options); // 不需要添加 token，直接放行
        }
        // 1.获取本地存储的 accessToken
        String? accessToken = _authRepository.accessToken;

        // 2.token为空，转跳登录
        if (accessToken == null || accessToken.isEmpty) {
          await popError('请重新登录', '登录信息过期');
          navToLogin();
        }
        //  3.检查令牌有效期
        if (_shouldRefreshToken()) {
          return _handleTokenRefresh(options, handler);
        }

        options.headers['Authorization'] = accessToken;

        // print("Request: ${options.method} ${options.uri}"); // 打印请求信息
        return handler.next(options);
      },
      /**
       * 响应拦截器
       */
      onResponse: (response, handler) {
        return handler.next(response);
      },
      /**
       * 错误拦截
       */
      onError: (DioException err, handler) async {
        if (err.response != null && err.response!.data != null) {
          final statusCode = err.response?.statusCode;
          final message = err.response!.data['message'] ?? '未知错误';
          final requestUrl = err.requestOptions.uri.toString();
          final method = err.requestOptions.method;

          // 如果是 401 token过期，这里一定是refreshToken过期
          // 需要重新登录
          if (statusCode == 401) {
            await popError('请重新登录', '登录信息过期');
            navToLogin();
          }

          final customError = DioException(
            requestOptions: err.requestOptions,
            response: err.response,
            error: message,
          );

          // ignore: avoid_print
          print(
              'Error $statusCode: $message, Method: $method, URL: $requestUrl');
          return handler.next(customError);
        } else {
          print(err);
          return handler.next(err);
        }
      },
    ));
  }

  // 4. 私有 Dio 实例
  final Dio _dio = Dio(BaseOptions(
    baseUrl: AppConfig.baseUrl,
    connectTimeout: const Duration(milliseconds: 10000),
    receiveTimeout: const Duration(milliseconds: 8000),
  ));

  // GET 请求，带查询参数
  Future<dynamic> get(String url, {Map<String, dynamic>? params}) async {
    return _request(() => _dio.get(url, queryParameters: params));
  }

  // POST 请求
  Future<dynamic> post(String url, Map<String, dynamic> data) async {
    return _request(() => _dio.post(url, data: data));
  }

  // PUT 请求
  Future<dynamic> put(String url, Map<String, dynamic> data) async {
    return _request(() => _dio.put(url, data: data));
  }

  // DELETE 请求
  Future<dynamic> delete(String url, {Map<String, dynamic>? data}) async {
    return _request(() => _dio.delete(url, data: data));
  }

  // 统一的请求处理方法
  Future<Map<String, dynamic>> _request(
      Future<Response> Function() request) async {
    try {
      final response = await request();
      print('检查响应：$response');
      return response.data; // 返回JSON格式的响应体
    } catch (e) {
      print('检查响应：$e');
      if (e is DioException) {
        throw ApiError(message: e.error.toString());
      }
      rethrow;
    }
  }

  // FormData 上传
  Future<dynamic> postFormData(String url, FormData formData) async {
    return _request(() => _dio.post(url, data: formData));
  }

  // 文件下载
  Future<void> downloadFile(
      String url, String savePath, void Function(int, int)? onProgress) async {
    try {
      _dio.download(
        url,
        savePath,
        onReceiveProgress: (received, total) {
          if (onProgress != null) onProgress(received, total);
        },
      );
    } catch (e) {
      if (e is DioException) {
        print('检查响应：$e');
        throw ApiError(message: '下载失败');
      }
      rethrow;
    }
  }

  // 检查是否是需要刷新
  _shouldRefreshToken() {
    return _authRepository.shouldRefreshToken();
  }

  // 带队列的令牌刷新处理
  Future<void> _handleTokenRefresh(
      RequestOptions options, RequestInterceptorHandler handler) async {
    // 已有刷新在进行
    if (_isRefreshing) {
      final completer = Completer<void>();
      _pendingRequests.add(completer);
      await completer.future;
      return _retryRequest(options, handler);
    }

    _isRefreshing = true;
    try {
      await _refreshToken();
      // 刷新成功后重试所有等待请求
      for (var c in _pendingRequests) {
        c.complete();
      }
      _pendingRequests.clear();
      return _retryRequest(options, handler);
    } catch (e) {
      // 刷新失败处理
      await _handleTokenInvalid();
      return handler.reject(
          DioException(requestOptions: options, error: 'Token refresh failed'));
    } finally {
      _isRefreshing = false;
    }
  }

  // 请求重试逻辑
  Future<void> _retryRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    try {
      final newOptions = options.copyWith(headers: {
        ...options.headers,
        'Authorization': _authRepository.accessToken
      });
      final response = await _dio.fetch(newOptions);
      handler.resolve(response);
    } on DioException catch (e) {
      handler.reject(e);
    }
  }

  // 令牌刷新方法
  Future<void> _refreshToken() async {
    final refreshToken = _authRepository.refreshToken;
    if (refreshToken == null) {
      await _handleTokenInvalid();
      throw Exception('No refresh token available');
    }

    try {
      // 使用新实例避免拦截器
      final dio = Dio(_dio.options);
      final response =
          await dio.post('/auth/refresh', data: {'refreshToken': refreshToken});

      await _authRepository.setToken(
          accessToken: response.data['access_token'],
          expired: response.data['expired']);
    } on DioException catch (e) {
      await _handleTokenInvalid();
    }
  }

  // 统一处理令牌失效
  Future<void> _handleTokenInvalid() async {
    await _authRepository.celarTokens();
    navToLogin();
    for (var c in _pendingRequests) {
      c.completeError('Token invalid');
    }
    _pendingRequests.clear();
  }

  // 登录页面
  Future<void> navToLogin() async {
    try {
      Loading.show(message: '正在登出');
      // 设置为需要重新刷新本地数据
      await _authRepository.setLocalStore(false);
      if (kIsWeb) {
        RouteManager.replaceWith('/web-login');
      } else {
        RouteManager.replaceWith('/mobile-login');
      }
    } catch (e) {
      print(e);
    } finally {
      Loading.hide();
    }
  }
}

// 自定义错误类型，用于处理服务端返回的响应体错误
class ApiError implements Exception {
  final String message;

  ApiError({required this.message});

  @override
  String toString() {
    return message;
  }
}
